#include "my_log.h"
#include "my_str.h"
#include "my_hrtime.h"
#include <stdarg.h>

my_log_t g_log = {
    .log_file       = NULL,
    .fd             = STDOUT_FILENO,
    .single         = MY_SPINLOCK_INITLIZER,
    .rwlock         = MY_RWLOCK_INITLIZER,
    .last_rolling   = 0,
};

void
my_log_file_rolling()
{
    int64_t cur_time = get_cache_time();
    int64_t cur_time_sec = my_usec_2_sec(cur_time);
    g_log.last_rolling = cur_time_sec;

    const char *log_file = g_log.log_file;
    size_t n = strlen(log_file);
    if (n + MY_LOG_FILE_TIME_SIZE > MY_MAX_LOG_SIZE) {
        assert("log file name is to long");
    }

    char rolling_name[MY_MAX_LOG_SIZE];
    my_copy(rolling_name, log_file, (int64_t)n);
    rolling_name[n] = '-';
    n++;

    my_log_file_time(rolling_name + n, (int64_t)(MY_MAX_LOG_SIZE - n));

    // try create new file
    creat(rolling_name, 0644);
    // try remove symlink
    remove(log_file);
    // try create symlink
    symlink(rolling_name, log_file);


    int fd = open(log_file, O_APPEND|O_CLOEXEC|O_WRONLY);
    if (fd < 0) {
        return;
    }

    int old_fd;
    my_rwlock_wlock(&g_log.rwlock);
    old_fd = g_log.fd;
    g_log.fd = fd;
    my_rwlock_wunlock(&g_log.rwlock);

    if (old_fd != STDOUT_FILENO && old_fd != INVALID_FD) {
        close(old_fd);
    }
}

void
my_log_file_try_rolling()
{
    int64_t cur_time = get_cache_time();
    int64_t cur_time_sec = my_usec_2_sec(cur_time);
    my_spinlock_lock(&g_log.single);
    if (g_log.log_file != NULL &&
        cur_time_sec - g_log.last_rolling > g_log.rolling_sec)
    {
        my_log_file_rolling();
    }
    my_spinlock_unlock(&g_log.single);
}

void
my_log_init(my_log_level_t level, const char *log_file, int64_t rolling_sec)
{
    g_log.level = level;
    g_log.log_file = log_file;
    if (rolling_sec < MY_LOG_MIN_ROLLING_SEC) {
        rolling_sec = MY_LOG_MIN_ROLLING_SEC;
    }
    g_log.rolling_sec = rolling_sec;

    if (log_file == NULL) {
        g_log.fd = STDOUT_FILENO;
        return;
    }

    my_log_file_try_rolling();
}

void
my_log_output(const char *level_str, const char *file,
    int line, my_bool_t abort_proc, const char *fmt, ...)
{
    char str[MY_MAX_LOG_SIZE];
    size_t str_size = MY_MAX_LOG_SIZE - 1;
    int n = snprintf(str, str_size, "[%s] %s:%d ",
        level_str, my_path_base(file), line);
    va_list args;
    va_start(args, fmt);
    char *end = my_vslprintf(str + n, str + str_size - n, fmt, args);
    size_t m = (size_t)(end - str);
    va_end(args);
    str[m] = '\n';
    m++;

    my_rwlock_rlock(&g_log.rwlock);
    write(g_log.fd, str, m);
    my_rwlock_runlock(&g_log.rwlock);

    if (abort_proc) {
        abort();
    }
}